#include "GL.h"

// TODO: optimize stencil functions (add & save theirs state)
namespace Rz { namespace Graphics { namespace Render { namespace Libraries { namespace OpenGL {

GLState								GL::_GLState				= { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
GLState								GL::_GLStateStack[16];
u8									GL::_GLStateStackPointer    = 0;

// GL Arrays
PFNGLGENBUFFERSPROC					GL::_genBuffers				= nullptr;
PFNGLBINDBUFFERPROC					GL::_bindBuffer				= nullptr;
PFNGLBUFFERDATAPROC					GL::_bufferData				= nullptr;
PFNGLBUFFERSUBDATAPROC				GL::_bufferSubData			= nullptr;
PFNGLDELETEBUFFERSPROC				GL::_deleteBuffers			= nullptr;

// GL Shaders
PFNGLCREATESHADERPROC				GL::_createShader			= nullptr;
PFNGLSHADERSOURCEPROC				GL::_shaderSource			= nullptr;
PFNGLCOMPILESHADERPROC				GL::_compileShader			= nullptr;
PFNGLISSHADERPROC					GL::_isShader				= nullptr;
PFNGLGETSHADERINFOLOGPROC			GL::_getShaderInfoLog		= nullptr;
PFNGLGETPROGRAMINFOLOGPROC			GL::_getProgramInfoLog		= nullptr;
PFNGLCREATEPROGRAMPROC				GL::_createProgram			= nullptr;
PFNGLATTACHSHADERPROC				GL::_attachShader			= nullptr;
PFNGLLINKPROGRAMPROC				GL::_linkProgram			= nullptr;
PFNGLUSEPROGRAMPROC					GL::_useProgram				= nullptr;
PFNGLGETATTRIBLOCATIONPROC			GL::_getAttribLocation		= nullptr;
PFNGLGETUNIFORMLOCATIONPROC			GL::_getUniformLocation		= nullptr;
PFNGLDELETESHADERPROC				GL::_deleteShader			= nullptr;
PFNGLDELETEPROGRAMPROC				GL::_deleteProgram			= nullptr;
PFNGLUNIFORM1FPROC					GL::_uniform1f				= nullptr;
PFNGLUNIFORM1IPROC					GL::_uniform1i				= nullptr;
PFNGLUNIFORM2FVPROC					GL::_uniform2fv				= nullptr;
PFNGLUNIFORM2IVPROC					GL::_uniform2iv				= nullptr;
PFNGLUNIFORM3FVPROC					GL::_uniform3fv				= nullptr;
PFNGLUNIFORM3IVPROC					GL::_uniform3iv				= nullptr;
PFNGLUNIFORM4FVPROC					GL::_uniform4fv				= nullptr;
PFNGLUNIFORM4IVPROC					GL::_uniform4iv				= nullptr;
PFNGLUNIFORMMATRIX3FVPROC			GL::_uniformMatrix3f		= nullptr;
PFNGLUNIFORMMATRIX4FVPROC			GL::_uniformMatrix4f		= nullptr;

// VBO
PFNGLGENVERTEXARRAYSPROC			GL::_genVertexArrays		= nullptr;
PFNGLBINDVERTEXARRAYPROC			GL::_bindVertexArray		= nullptr;
PFNGLDELETEVERTEXARRAYSPROC			GL::_deleteVertexArrays		= nullptr;
PFNGLVERTEXATTRIBPOINTERPROC		GL::_vertexAttribPointer	= nullptr;
PFNGLENABLEVERTEXATTRIBARRAYPROC	GL::_enableVertexAttribArray= nullptr;
PFNGLDRAWARRAYSPROC					GL::_drawArrays				= nullptr;
PFNGLDRAWELEMENTSPROC				GL::_drawElements			= nullptr;

// FBO
PFNGLGENFRAMEBUFFERSPROC			GL::_genFramebuffers		= nullptr;
PFNGLBINDFRAMEBUFFERPROC			GL::_bindFramebuffer		= nullptr;
PFNGLDELETEFRAMEBUFFERSPROC			GL::_deleteFramebuffers		= nullptr;
PFNGLCHECKFRAMEBUFFERSTATUSPROC		GL::_checkFramebufferStatus	= nullptr;
PFNGLFRAMEBUFFERTEXTUREPROC			GL::_framebufferTexture		= nullptr;
PFNGLFRAMEBUFFERTEXTURE1DPROC		GL::_framebufferTexture1D	= nullptr;
PFNGLFRAMEBUFFERTEXTURE2DPROC		GL::_framebufferTexture2D	= nullptr;
PFNGLFRAMEBUFFERTEXTURE3DPROC		GL::_framebufferTexture3D	= nullptr;
PFNGLFRAMEBUFFERRENDERBUFFERPROC	GL::_framebufferRenderbuffer= nullptr;

// Renderbuffer
PFNGLGENRENDERBUFFERSPROC			GL::_genRenderbuffers		= nullptr;
PFNGLDELETERENDERBUFFERSPROC		GL::_deleteRenderbuffers	= nullptr;
PFNGLBINDRENDERBUFFERPROC			GL::_bindRenderbuffer		= nullptr;
PFNGLRENDERBUFFERSTORAGEPROC		GL::_renderbufferStorage	= nullptr;

// Textures
PFNGLACTIVETEXTUREPROC				GL::_activeTexture			= nullptr;
PFNGLTEXIMAGE3DPROC					GL::_texImage3D				= nullptr;
PFNGLGENERATEMIPMAPPROC				GL::_generateMipmap			= nullptr;

// Samplers
PFNGLGENSAMPLERSPROC				GL::_genSamplers			= nullptr;
PFNGLDELETESAMPLERSPROC				GL::_deleteSamplers			= nullptr;
PFNGLBINDSAMPLERPROC				GL::_bindSampler			= nullptr;
PFNGLSAMPLERPARAMETERIPROC			GL::_samplerParameter		= nullptr;

// VSync
PFNWGLSWAPINTERVALEXTPROC			GL::_swapInterval			= nullptr;
PFNWGLGETSWAPINTERVALEXTPROC		GL::_getSwapInterval		= nullptr;

// MRT
PFNGLDRAWBUFFERPROC					GL::_drawBuffer				= nullptr;
PFNGLDRAWBUFFERSPROC				GL::_drawBuffers			= nullptr;

// GL Main & Deprecated
PFNGLBLENDEQUATIONPROC				GL::_blendEquation			= nullptr;
PFNGLSTENCILOPSEPARATEPROC			GL::_stencilOpSeparate		= nullptr;
PFNGLSTENCILFUNCSEPARATEPROC		GL::_stencilFuncSeparate	= nullptr;


bool GL::Init()
{
	// GL Arrays
	_genBuffers					= (PFNGLGENBUFFERSPROC)				wglGetProcAddress("glGenBuffers");
	_bindBuffer					= (PFNGLBINDBUFFERPROC)				wglGetProcAddress("glBindBuffer");
	_bufferData					= (PFNGLBUFFERDATAPROC)				wglGetProcAddress("glBufferData");
	_bufferSubData				= (PFNGLBUFFERSUBDATAPROC)			wglGetProcAddress("glBufferSubData");
	_deleteBuffers				= (PFNGLDELETEBUFFERSPROC)			wglGetProcAddress("glDeleteBuffers");

	// GL Shaders
	_createShader				= (PFNGLCREATESHADERPROC)			wglGetProcAddress("glCreateShader");
	_shaderSource				= (PFNGLSHADERSOURCEPROC)			wglGetProcAddress("glShaderSource");
	_compileShader				= (PFNGLCOMPILESHADERPROC)			wglGetProcAddress("glCompileShader");
	_isShader					= (PFNGLISSHADERPROC)				wglGetProcAddress("glIsShader");

	_getShaderInfoLog			= (PFNGLGETSHADERINFOLOGPROC)		wglGetProcAddress("glGetShaderInfoLog");
	_getProgramInfoLog			= (PFNGLGETPROGRAMINFOLOGPROC)		wglGetProcAddress("glGetProgramInfoLog");
	_createProgram				= (PFNGLCREATEPROGRAMPROC)			wglGetProcAddress("glCreateProgram");
	_attachShader				= (PFNGLATTACHSHADERPROC)			wglGetProcAddress("glAttachShader");
	_linkProgram				= (PFNGLLINKPROGRAMPROC)			wglGetProcAddress("glLinkProgram");
	_useProgram					= (PFNGLUSEPROGRAMPROC)				wglGetProcAddress("glUseProgram");
	_getAttribLocation			= (PFNGLGETATTRIBLOCATIONPROC)		wglGetProcAddress("glGetAttribLocation");
	_getUniformLocation			= (PFNGLGETUNIFORMLOCATIONPROC)		wglGetProcAddress("glGetUniformLocation");
	_deleteShader				= (PFNGLDELETESHADERPROC)			wglGetProcAddress("glDeleteShader");
	_deleteProgram				= (PFNGLDELETEPROGRAMPROC)			wglGetProcAddress("glDeleteProgram");
	_uniform1f					= (PFNGLUNIFORM1FPROC)				wglGetProcAddress("glUniform1f");
	_uniform1i					= (PFNGLUNIFORM1IPROC)				wglGetProcAddress("glUniform1i");
	_uniform2fv					= (PFNGLUNIFORM2FVPROC)				wglGetProcAddress("glUniform2fv");
	_uniform2iv					= (PFNGLUNIFORM2IVPROC)				wglGetProcAddress("glUniform2iv");
	_uniform3fv					= (PFNGLUNIFORM3FVPROC)				wglGetProcAddress("glUniform3fv");
	_uniform3iv					= (PFNGLUNIFORM3IVPROC)				wglGetProcAddress("glUniform3iv");
	_uniform4fv					= (PFNGLUNIFORM4FVPROC)				wglGetProcAddress("glUniform4fv");
	_uniform4iv					= (PFNGLUNIFORM4IVPROC)				wglGetProcAddress("glUniform4iv");
	_uniformMatrix3f			= (PFNGLUNIFORMMATRIX3FVPROC)		wglGetProcAddress("glUniformMatrix3fv");
	_uniformMatrix4f			= (PFNGLUNIFORMMATRIX4FVPROC)		wglGetProcAddress("glUniformMatrix4fv");

	// VBO
	_genVertexArrays			= (PFNGLGENVERTEXARRAYSPROC)		wglGetProcAddress("glGenVertexArrays");
	_bindVertexArray			= (PFNGLBINDVERTEXARRAYPROC)		wglGetProcAddress("glBindVertexArray");
	_deleteVertexArrays			= (PFNGLDELETEVERTEXARRAYSPROC)		wglGetProcAddress("glDeleteVertexArrays");
	_vertexAttribPointer		= (PFNGLVERTEXATTRIBPOINTERPROC)	wglGetProcAddress("glVertexAttribPointer");
	_enableVertexAttribArray	= (PFNGLENABLEVERTEXATTRIBARRAYPROC)wglGetProcAddress("glEnableVertexAttribArray");
	_drawArrays					= (PFNGLDRAWARRAYSPROC)				wglGetProcAddress("glDrawArrays");
	_drawElements				= (PFNGLDRAWELEMENTSPROC)			wglGetProcAddress("glDrawElements");

	// FBO
	_genFramebuffers			= (PFNGLGENFRAMEBUFFERSPROC)		wglGetProcAddress("glGenFramebuffers");
	_bindFramebuffer			= (PFNGLBINDFRAMEBUFFERPROC)		wglGetProcAddress("glBindFramebuffer");
	_deleteFramebuffers			= (PFNGLDELETEFRAMEBUFFERSPROC)		wglGetProcAddress("glDeleteFramebuffers");
	_checkFramebufferStatus		= (PFNGLCHECKFRAMEBUFFERSTATUSPROC)	wglGetProcAddress("glCheckFramebufferStatus");
	_framebufferTexture			= (PFNGLFRAMEBUFFERTEXTUREPROC)		wglGetProcAddress("glFramebufferTexture");
	_framebufferTexture1D		= (PFNGLFRAMEBUFFERTEXTURE1DPROC)	wglGetProcAddress("glFramebufferTexture1D");
	_framebufferTexture2D		= (PFNGLFRAMEBUFFERTEXTURE2DPROC)	wglGetProcAddress("glFramebufferTexture2D");
	_framebufferTexture3D		= (PFNGLFRAMEBUFFERTEXTURE3DPROC)	wglGetProcAddress("glFramebufferTexture3D");
	_framebufferRenderbuffer	= (PFNGLFRAMEBUFFERRENDERBUFFERPROC)wglGetProcAddress("glFramebufferRenderbuffer");

	// Renderbuffer
	_genRenderbuffers			= (PFNGLGENRENDERBUFFERSPROC)		wglGetProcAddress("glGenRenderbuffers");
	_deleteRenderbuffers		= (PFNGLDELETERENDERBUFFERSPROC)	wglGetProcAddress("glDeleteRenderbuffers");
	_bindRenderbuffer			= (PFNGLBINDRENDERBUFFERPROC)		wglGetProcAddress("glBindRenderbuffer");
	_renderbufferStorage		= (PFNGLRENDERBUFFERSTORAGEPROC)	wglGetProcAddress("glRenderbufferStorage");

	// Textures
	_activeTexture				= (PFNGLACTIVETEXTUREPROC)			wglGetProcAddress("glActiveTexture");
	_texImage3D					= (PFNGLTEXIMAGE3DPROC)				wglGetProcAddress("glTexImage3D");
	_generateMipmap				= (PFNGLGENERATEMIPMAPPROC)			wglGetProcAddress("glGenerateMipmap");

	// Samplers
	_genSamplers				= (PFNGLGENSAMPLERSPROC)			wglGetProcAddress("glGenSamplers");
	_deleteSamplers				= (PFNGLDELETESAMPLERSPROC)			wglGetProcAddress("glDeleteSamplers");
	_bindSampler				= (PFNGLBINDSAMPLERPROC)			wglGetProcAddress("glBindSampler");
	_samplerParameter			= (PFNGLSAMPLERPARAMETERIPROC)		wglGetProcAddress("glSamplerParameteri");

	// VSync
	_swapInterval				= (PFNWGLSWAPINTERVALEXTPROC)		wglGetProcAddress("wglSwapIntervalEXT");
	_getSwapInterval			= (PFNWGLGETSWAPINTERVALEXTPROC)	wglGetProcAddress("wglGetSwapIntervalEXT");

	// MRT
	_drawBuffer					= glDrawBuffer;
	_drawBuffers				= (PFNGLDRAWBUFFERSPROC)			wglGetProcAddress("glDrawBuffers");

	// GL Main & Deprecated
	_blendEquation				= (PFNGLBLENDEQUATIONPROC)			wglGetProcAddress("glBlendEquation");
	_stencilOpSeparate			= (PFNGLSTENCILOPSEPARATEPROC)		wglGetProcAddress("glStencilOpSeparate");
	_stencilFuncSeparate		= (PFNGLSTENCILFUNCSEPARATEPROC)	wglGetProcAddress("glStencilFuncSeparate");

	return true;
}

// GL Arrays
GLuint GL::GenBuffer()
{
	GLuint result;
	_genBuffers(1, &result);
	return result;
}

void GL::BindBuffer(GLenum target, GLuint buffer)
{
	if (buffer == _GLState.LastBuffer)
	{
		return;
	}

	_GLState.LastBuffer = buffer;
	_bindBuffer(target, buffer);
}

void GL::BufferData(GLenum target, GLsizeiptr size, const GLvoid* data, GLenum usage)
{
	_bufferData(target, size, data, usage);
}


void GL::BufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid* data)
{
	_bufferSubData(target, offset, size, data);
}

void GL::DeleteBuffer(GLuint buffer)
{
	if (_GLState.LastBuffer == buffer)
	{
		_GLState.LastBuffer = 0;
	}

	_deleteBuffers(1, &buffer);
}

// GL Shaders
GLuint GL::CreateShader(GLenum type)
{
	return _createShader(type);
}

void GL::ShaderSource(GLuint shader, GLsizei count, const GLchar** string, const GLint* length)
{
	_shaderSource(shader, count, string, length);
}

void GL::CompileShader(GLuint shader)
{
	_compileShader(shader);
}

GLboolean GL::IsShader(GLuint shader)
{
	return _isShader(shader);
}

void GL::GetShaderInfoLog(GLuint shader, GLsizei buffSize, GLsizei* length, GLchar* infoLog)
{
	_getShaderInfoLog(shader, buffSize, length, infoLog);
}

GLuint GL::CreateProgram()
{
	return _createProgram();
}

void GL::AttachShader(GLuint program, GLuint shader)
{
	_attachShader(program, shader);
}

void GL::LinkProgram(GLuint program)
{
	_linkProgram(program);
}

void GL::UseProgram(GLuint program)
{
	if (program == _GLState.LastProgramUsed)
	{
		return;
	}

	_GLState.LastProgramUsed = program;
	_useProgram(program);
}

void GL::GetProgramInfoLog(GLuint program, GLsizei buffSize, GLsizei* length, GLchar* infoLog)
{
	_getProgramInfoLog(program, buffSize, length, infoLog);
}

GLint GL::GetAttribLocation(GLuint program, const GLchar* name)
{
	return _getAttribLocation(program, name);
}

GLint GL::GetUniformLocation(GLuint program, const GLchar* name)
{
	return _getUniformLocation(program, name);
}

void GL::DeleteShader(GLuint shader)
{
	_deleteShader(shader);
}

void GL::DeleteProgram(GLuint program)
{
	_deleteShader(program);
}

void GL::Uniform1f(GLint location, GLfloat value)
{
	_uniform1f(location, value);
}

void GL::Uniform1i(GLint location, GLint value)
{
	_uniform1i(location, value);
}

void GL::Uniform2fv(GLint location, const GLfloat* value)
{
	_uniform2fv(location, 1, value);
}

void GL::Uniform2iv(GLint location, const GLint* value)
{
	_uniform2iv(location, 1, value);
}

void GL::Uniform3fv(GLint location, const GLfloat* value)
{
	_uniform3fv(location, 1, value);
}

void GL::Uniform3iv(GLint location, const GLint* value)
{
	_uniform3iv(location, 1, value);
}

void GL::Uniform4fv(GLint location, const GLfloat* value)
{
	_uniform4fv(location, 1, value);
}

void GL::Uniform4iv(GLint location, const GLint* value)
{
	_uniform4iv(location, 1, value);
}

void GL::UniformMatrix3f(GLint location, const GLfloat* value)
{
	_uniformMatrix3f(location, 1, GL_FALSE, value);
}

void GL::UniformMatrix4f(GLint location, const GLfloat* value)
{
	_uniformMatrix4f(location, 1, GL_FALSE, value);
}

// VBO
GLuint GL::GenVertexArray()
{
	GLuint result;
	_genVertexArrays(1, &result);
	return result;
}

void GL::BindVertexArray(GLuint array)
{
	if (array == _GLState.LastVertexArray)
	{
		return;
	}

	_GLState.LastVertexArray = array;
	_bindVertexArray(array);
}

void GL::DeleteVertexArray(GLuint array)
{
	_deleteVertexArrays(1, &array);
}

void GL::VertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid* pointer)
{
	_vertexAttribPointer(index, size, type, normalized, stride, pointer);
}

void GL::EnableVertexAttribArray(GLuint index)
{
	_enableVertexAttribArray(index);
}

void GL::DrawArrays(GLenum mode, GLint first, GLsizei count)
{
	_drawArrays(mode, first, count);
}

void GL::DrawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid* indices)
{
	_drawElements(mode, count, type, indices);
}

// FBO
GLuint GL::GenFramebuffer()
{
	GLuint result;
	_genFramebuffers(1, &result);
	return result;
}

void GL::BindFramebuffer(GLuint framebuffer)
{
	_bindFramebuffer(GL_FRAMEBUFFER, framebuffer);
}

void GL::DeleteFramebuffer(GLuint framebuffer)
{
	_deleteFramebuffers(1, &framebuffer);
}

GLenum GL::CheckFramebufferStatus(GLenum target)
{
	return _checkFramebufferStatus(target);
}

void GL::FramebufferTexture(GLenum attachment, GLuint texture, GLint level)
{
	_framebufferTexture(GL_FRAMEBUFFER, attachment, texture, level);
}

void GL::FramebufferTexture1D(GLenum attachment, GLenum textarget, GLuint texture, GLint level)
{
	_framebufferTexture1D(GL_FRAMEBUFFER, attachment, textarget, texture, level);
}

void GL::FramebufferTexture2D(GLenum attachment, GLenum textarget, GLuint texture, GLint level)
{
	_framebufferTexture2D(GL_FRAMEBUFFER, attachment, textarget, texture, level);
}

void GL::FramebufferTexture3D(GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset)
{
	_framebufferTexture3D(GL_FRAMEBUFFER, attachment, textarget, texture, level, zoffset);
}

void GL::FramebufferRenderbuffer(GLenum attachment, GLuint renderbuffer)
{
	_framebufferRenderbuffer(GL_FRAMEBUFFER, attachment, GL_RENDERBUFFER, renderbuffer);
}

// Renderbuffer
GLuint GL::GenRenderbuffer()
{
	GLuint result;
	_genRenderbuffers(1, &result);
	return result;
}

void GL::BindRenderbuffer(GLuint renderbuffer)
{
	_bindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
}

void GL::DeleteRenderbuffer(GLuint renderbuffer)
{
	_deleteRenderbuffers(1, &renderbuffer);
}

void GL::RenderbufferStorage(GLenum internalformat, GLsizei width, GLsizei height)
{
	_renderbufferStorage(GL_RENDERBUFFER, internalformat, width, height);
}

// Textures
GLuint GL::GenTexture()
{
	GLuint result;
	glGenTextures(1, &result);
	return result;
}

void GL::BindTexture(GLenum target, GLuint texture)
{
// 	if (texture == m_GLState.LastTexture)
// 		return;
// 
// 	m_GLState.LastTexture = texture;
	glBindTexture(target, texture);
}

void GL::DeleteTexture(GLuint texture)
{
// 	if (texture == m_GLState.LastTexture)
// 		m_GLState.LastTexture = 0;

	glDeleteTextures(1, &texture);
}

void GL::ActiveTexture(GLenum texture)
{
	if (texture == _GLState.ActiveTexture)
	{
		return;
	}

	_GLState.ActiveTexture = texture;
	_activeTexture(texture);
}

void GL::TexImage1D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const GLvoid* pixels)
{
	glTexImage1D(target, level, internalformat, width, 0, format, type, pixels);
}

void GL::TexImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid* pixels)
{
	glTexImage2D(target, level, internalformat, width, height, 0, format, type, pixels);
}

void GL::TexImage3D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid* pixels)
{
	_texImage3D(target, level, internalformat, width, height, depth, 0, format, type, pixels);
}

void GL::TexParameterf(GLenum target, GLenum pname, GLfloat param)
{
	glTexParameterf(target, pname, param);
}

void GL::TexParameteri(GLenum target, GLenum pname, GLint param)
{
	glTexParameteri(target, pname, param);
}

void GL::GenerateMipmap(GLenum target)
{
	_generateMipmap(target);
}

// Samplers
GLuint GL::GenSampler()
{
	GLuint result;
	_genSamplers(1, &result);
	return result;
}

void GL::BindSampler(GLenum target, GLuint sampler)
{
	// 	if ( == m_GLState.LastTexture)
	// 		return;
	// 
	// 	m_GLState.LastTexture = texture;
	_bindSampler(target, sampler);
}

void GL::DeleteSampler(GLuint sampler)
{
	// 	if (texture == m_GLState.LastTexture)
	// 		m_GLState.LastTexture = 0;
	_deleteSamplers(1, &sampler);
}

void GL::SamplerParameter(GLuint sampler, GLenum pname, GLint param)
{
	_samplerParameter(sampler, pname, param);
}

// VSync
bool GL::SwapInterval(u32 interval)
{
	return !!_swapInterval(interval);
}

int GL::GetSwapInterval()
{
	return _getSwapInterval();
}

// MRT
void GL::DrawBuffer(GLenum mode)
{
	_drawBuffer(mode);
}

void GL::DrawBuffers(GLsizei n, const GLenum* bufs)
{
	_drawBuffers(n, bufs);
}

// Main
void GL::Enable(GLenum cap)
{
	glEnable(cap);
}

void GL::Disable(GLenum cap)
{
	glDisable(cap);
}

void GL::CullFace(GLenum mode)
{
	if (mode == _GLState.CullFace || mode == 0)
		return;

	_GLState.CullFace = mode;
	glCullFace(mode);
}

void GL::FrontFace(GLenum mode)
{
	glFrontFace(mode);
}

void GL::Clear(GLbitfield mask)
{
	glClear(mask);
}

void GL::ClearColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha)
{
	glClearColor(red, green, blue, alpha);
}

void GL::ClearDepth(GLclampf depth)
{
	glClearDepth(depth);
}

void GL::DepthMask(GLboolean flag)
{
	glDepthMask(flag);
}

void GL::DepthFunc(GLenum func)
{
	if (func == _GLState.DepthFunc || func == 0)
	{
		return;
	}

	_GLState.DepthFunc = func;
	glDepthFunc(func);
}

void GL::StencilMask(GLuint mask)
{
	glStencilMask(mask);
}

void GL::StencilOp(GLenum sfail, GLenum dpfail, GLenum dppass)
{
	glStencilOp(sfail, dpfail, dppass);
}

void GL::StencilOpSeparate(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass)
{
	_stencilOpSeparate(face, sfail, dpfail, dppass);
}

void GL::StencilFunc(GLenum func, GLint ref, GLuint mask)
{
	glStencilFunc(func, ref, mask);
}

void GL::StencilFuncSeparate(GLenum face, GLenum func, GLint ref, GLuint mask)
{
	_stencilFuncSeparate(face, func, ref, mask);
}

void GL::BlendFunc(GLenum sfactor, GLenum dfactor)
{
	if ((_GLState.BlendFuncSrcFactor == sfactor && _GLState.BlendFuncDestFactor == dfactor) ||
		sfactor == 0 || dfactor == 0)
	{
		return;
	}

	_GLState.BlendFuncSrcFactor = sfactor;
	_GLState.BlendFuncDestFactor = dfactor;
	glBlendFunc(sfactor, dfactor);
}

void GL::BlendEquation(GLenum mode)
{
	if (mode == _GLState.BlendEquation || mode == 0)
	{
		return;
	}

	_GLState.BlendEquation = mode;
	_blendEquation(mode);
}

void GL::PointSize(GLfloat size)
{
	glPointSize(size);
}

void GL::PolygonMode(GLenum face, GLenum mode)
{
	if ((face == _GLState.PolygonFace && mode == _GLState.PolygonMode) ||
		face == 0 || mode == 0)
	{
		return;
	}

	_GLState.PolygonFace = face;
	_GLState.PolygonMode = mode;

	glPolygonMode(face, mode);
}

void GL::Viewport(GLint x, GLint y, GLsizei width, GLsizei height)
{
	if (_GLState.ViewPortX == x &&
		_GLState.ViewPortY == y &&
		_GLState.ViewPortWidth == width &&
		_GLState.ViewPortHeight == height)
	{
		return;
	}

	_GLState.ViewPortX = x;
	_GLState.ViewPortY = y;
	_GLState.ViewPortWidth = width;
	_GLState.ViewPortHeight = height;

	glViewport(x, y, width, height);
}

bool GL::PushState()
{
// 	if (m_GLStateStackPointer != 16)
// 	{
// 		m_GLStateStack[m_GLStateStackPointer] = m_GLState;
// 
// 		m_GLStateStackPointer++;
// 		return true;
// 	}
// 	else
// 	{
// 		return false;
// 	}
	return true;
}

bool GL::PopState()
{
// 	if (m_GLStateStackPointer != 0)
// 	{
// 		m_GLStateStackPointer--;
// 
// 		BlendEquation(m_GLStateStack[m_GLStateStackPointer].BlendEquation);
// 		BlendFunc(m_GLStateStack[m_GLStateStackPointer].BlendFuncSrcFactor, m_GLStateStack[m_GLStateStackPointer].BlendFuncDestFactor);
// 		DepthFunc(m_GLStateStack[m_GLStateStackPointer].DepthFunc);
// 		CullFace(m_GLStateStack[m_GLStateStackPointer].CullFace);
// 		Viewport(m_GLStateStack[m_GLStateStackPointer].ViewPortX,
// 			m_GLStateStack[m_GLStateStackPointer].ViewPortY,
// 			m_GLStateStack[m_GLStateStackPointer].ViewPortWidth,
// 			m_GLStateStack[m_GLStateStackPointer].ViewPortHeight);
// 
// 		return true;
// 	}
// 	else
// 	{
// 		return false;
// 	}
	return true;
}

} } } } }
